#pragma once

#include <set>
#include <string>
#include <algorithm>
#include <iterator>
#include <unordered_set>
#include <unordered_map>

#include <range/v3/range.hpp>

namespace OMIT_NAMESPACE
{
	namespace detail
	{
		template <typename, typename = void>
		constexpr bool _OMIT_RANGE_CHECK = false;

		template <typename U>
		constexpr bool _OMIT_RANGE_CHECK<
			U,
			std::void_t<decltype(ranges::begin(*std::declval<U*>()),
								 ranges::end(*std::declval<U*>()))>> = true;

		template <typename T>
		constexpr bool OMIT_RANGE_CONCEPT = _OMIT_RANGE_CHECK<T>;
	}
}

namespace OMIT_NAMESPACE
{
	template<typename Traits>
	template<typename T, typename... Args>
	T* TMesh<Traits>::New(Args&&... args) {
		T* elem = (T*)_resourcePool.allocate(sizeof(T), alignof(T));
		new (elem) T(std::forward<Args>(args)...);
		GetComponentSet<T>().insert(elem);
		return elem;
	}

	// clear and erase
	template<typename Traits>
	template<typename T>
	void TMesh<Traits>::Delete(T* elem) {
#ifndef NDEBUG
		elem->Reset();
#endif // !NDEBUG
		_resourcePool.deallocate(elem, sizeof(T), alignof(T));
		GetComponentSet<T>().erase(elem);
	}

	template<typename Traits>
	std::vector<size_t> TMesh<Traits>::Indices(TF* f) const {
		assert(f != nullptr);
		std::vector<size_t> indices;
		for (auto* v : f->AdjVertices())
			indices.push_back(Index(v));
		return indices;
	}

	template<typename Traits>
	template<typename... Args>
	MeshTraits_E<Traits>* TMesh<Traits>::AddEdge(TV* v0, TV* v1, Args&&... args) {
		assert(v0 != nullptr && v1 != nullptr && v0 != v1 && !TV::IsConnected(v0, v1));

		auto* e = New<TE>(std::forward<Args>(args)...);

		auto* h0 = New<TH>();
		auto* h1 = New<TH>();
		// 初始化
		e->SetHalfEdge(h0);

		h0->SetNext(h1);
		h0->SetPair(h1);
		h0->SetOrigin(v0);
		h0->SetEdge(e);

		h1->SetNext(h0);
		h1->SetPair(h0);
		h1->SetOrigin(v1);
		h1->SetEdge(e);

		// 连接 h0
		if (!v0->IsIsolated()) {
			auto* inV0 = v0->FindFreeIncident();
			if (inV0 == nullptr)
				return nullptr;
			auto* outV0 = inV0->Next();

			inV0->SetNext(h0);
			h1->SetNext(outV0);
		}
		else
			v0->SetHalfEdge(h0);

		// 连接 h1
		if (!v1->IsIsolated()) {
			auto* inV1 = v1->FindFreeIncident();
			if (inV1 == nullptr)
				return nullptr;
			auto* outV1 = inV1->Next();

			inV1->SetNext(h1);
			h0->SetNext(outV1);
		}
		else
			v1->SetHalfEdge(h1);

		return e;
	}

	template<typename Traits>
	template<typename HalfEdgeLoop, typename... Args>
	MeshTraits_F<Traits>* TMesh<Traits>::AddFace(const HalfEdgeLoop& hLoop, Args&&... args) {
		static_assert(OMIT_NAMESPACE::detail::OMIT_RANGE_CONCEPT<HalfEdgeLoop>);
		assert(ranges::begin(hLoop) != ranges::end(hLoop) && "hLoop must be non-empty");
		TH* h0 = *ranges::begin(hLoop);
#ifndef NDEBUG
		{
			TH* preHE = nullptr;
			for (auto* h : hLoop) {
				assert(h && h->IsFree());
				if (preHE)
					assert(preHE->End() == h->Origin());
				preHE = h;
			}
			assert(preHE->End() == h0->Origin());
		}
#endif // !NDEBUG

		// 重新排序连接关系
		{
			TH* preHE = nullptr;
			for (auto* h : hLoop) {
				if (preHE) {
					bool success = TH::MakeAdjacent(preHE, h);
					assert(success && "th polygon would introduce a non - monifold condition");
				}
				preHE = h;
			}
			bool success = TH::MakeAdjacent(preHE, h0);
			assert(success && "th polygon would introduce a non - monifold condition");
		}

		// 连接多边形面和 hLoop
		auto* f = New<TF>(std::forward<Args>(args)...);

		f->SetHalfEdge(h0);
		for (auto* h : hLoop)
			h->SetFace(f);

		return f;
	}

	template<typename Traits>
	void TMesh<Traits>::RemoveFace(TF* f) {
		assert(f != nullptr);
		for (auto* h : f->AdjHalfEdges())
			h->SetFace(nullptr);
		Delete<TF>(f);
	}

	template<typename Traits>
	void TMesh<Traits>::RemoveEdge(TE* e) {
		assert(e != nullptr);
		auto* h0 = e->HalfEdge();
		auto* h1 = h0->Pair();

		if (!h0->IsFree())
			RemoveFace(h0->Face());
		if (!h1->IsFree())
			RemoveFace(h1->Face());

		// init
		auto* v0 = h0->Origin();
		auto* v1 = h1->Origin();
		auto* inV0 = h0->Pre();
		auto* inV1 = h1->Pre();
		auto* outV0 = h0->RotateNext();
		auto* outV1 = h1->RotateNext();

		// [link off h0]
		if (v0->HalfEdge() == h0)
			v0->SetHalfEdge(outV0 == h0 ? nullptr : outV0);

		inV0->SetNext(outV0);

		// [link off h1]
		if (v1->HalfEdge() == h1)
			v1->SetHalfEdge(outV1 == h1 ? nullptr : outV1);

		inV1->SetNext(outV1);

		// delete
		Delete<TH>(h0);
		Delete<TH>(h1);
		Delete<TE>(e);
	}

	template<typename Traits>
	void TMesh<Traits>::RemoveVertex(TV* v) {
		assert(v != nullptr);
		auto view = v->OutHalfEdges();
		auto iter = view.begin();
		do {
			auto cpy_iter = iter;
			++iter;

			RemoveEdge(cpy_iter->Edge());
		} while (iter != view.end());
		Delete<TV>(v);
	}

	template<typename Traits>
	bool TMesh<Traits>::Init(const std::vector<std::vector<size_t>>& faces) {
		assert(!faces.empty());

		Clear();

		size_t max = 0;
		size_t min = std::numeric_limits<size_t>::max();
		for (const auto& f : faces) {
			assert(f.size() > 2);

			for (auto idx : f) {
				if (idx > max)
					max = idx;
				if (idx < min)
					min = idx;
			}
		}

		assert(min == 0);

		for (size_t i = 0; i <= max; i++)
			New<TV>();

		for (const auto& f : faces) {
			std::vector<TH*> hLoop;
			for (size_t i = 0; i < f.size(); i++) {
				size_t next = (i + 1) % f.size();
				assert(f[i] != f[next]);
				auto* u = _verts[f[i]];
				auto* v = _verts[f[next]];
				auto* h = u->HalfEdgeTo(v);
				if (!h)
					h = AddEdge(u, v)->HalfEdge();
				hLoop.push_back(h);
			}
			auto* f = AddFace(hLoop);
			assert(f != nullptr);
		}

		return true;
	}

	template<typename Traits>
	bool TMesh<Traits>::Init(const std::vector<size_t>& polygons, size_t sides) {
		assert(polygons.size() % sides == 0);
		std::vector<std::vector<size_t>> arrangedFaces;
		for (size_t i = 0; i < polygons.size(); i += sides) {
			std::vector<size_t> indices;
			for (size_t j = 0; j < sides; j++)
				indices.push_back(polygons[i + j]);
			arrangedFaces.push_back(std::move(indices));
		}
		return Init(arrangedFaces);
	}

	template<typename Traits>
	std::vector<std::vector<size_t>> TMesh<Traits>::Export() const {
		if (!IsValid())
			return {};

		std::vector<std::vector<size_t>> rst;
		for (auto* f : _faces.vec()) {
			std::vector<size_t> indices;
			for (auto* v : f->AdjVertices())
				indices.push_back(Index(v));
			rst.push_back(std::move(indices));
		}
		return rst;
	}

	template<typename Traits>
	void TMesh<Traits>::Clear() noexcept {
		for (auto* v : _verts.vec())
			_resourcePool.deallocate(v, sizeof(TV), alignof(TV));
		_verts.clear();

		for (auto* e : _edges.vec())
			_resourcePool.deallocate(e, sizeof(TE), alignof(TE));
		_edges.clear();

		for (auto* f : _faces.vec())
			_resourcePool.deallocate(f, sizeof(TF), alignof(TF));
		_faces.clear();

		for (auto* h : _halfEdges.vec())
			_resourcePool.deallocate(h, sizeof(TH), alignof(TH));
		_halfEdges.clear();

	}

	template<typename Traits>
	void TMesh<Traits>::Reserve(size_t n) {
		_verts.reserve(n);
		_edges.reserve(3 * n);
		_faces.reserve(2 * n);
		_halfEdges.reserve(6 * n);
	}

	template<typename Traits>
	bool TMesh<Traits>::HasBoundary() const noexcept {
		for (auto* h : _halfEdges) {
			if (h->IsOnBoundary())
				return true;
		}
		return false;
	}

	template<typename Traits>
	std::vector<MeshTraits_H<Traits>*> TMesh<Traits>::Boundaries() const {
		std::vector<TH*> boundaries;
		std::unordered_set<TH*> found;
		for (auto* h : _halfEdges) {
			if (h->IsOnBoundary() && found.find(h) == found.end()) {
				auto* cur = h;
				do {
					found.insert(cur);
					cur = cur->Next();
				} while (cur != h);
				boundaries.push_back(h);
			}
		}
		return boundaries;
	}

	template<typename Traits>
	bool TMesh<Traits>::IsValid() const {
		for (auto* h : _halfEdges) {
			if (!h->Next() || !h->Pair() || !h->Origin() || !h->Edge())
				return false;
		}

		std::unordered_set<TH*> unchckHalfEdges(_halfEdges.begin(), _halfEdges.end());
		TH* hadHE = nullptr;
		TH* curHE = nullptr;
		while (!unchckHalfEdges.empty() || hadHE != nullptr) {
			if (!hadHE) {
				auto iter = unchckHalfEdges.begin();
				hadHE = curHE = *iter;
				unchckHalfEdges.erase(iter);
			}
			curHE = curHE->Next();
			if (curHE == hadHE)
				hadHE = nullptr;
			else {
				auto target = unchckHalfEdges.find(curHE);
				if (target == unchckHalfEdges.end())
					return false;
				unchckHalfEdges.erase(target);
			}
		}

		for (auto* v : _verts) {
			for (auto* h : v->OutHalfEdges()) {
				if (h->Origin() != v)
					return false;
			}
		}

		for (auto* e : _edges) {
			if (!e->HalfEdge() || e->HalfEdge()->Edge() != e || e->HalfEdge()->Pair()->Edge() != e)
				return false;
		}

		for (auto* p : _faces) {
			if (!p->HalfEdge())
				return false;
			for (auto* h : p->AdjHalfEdges()) {
				if (h->Face() != p)
					return false;
			}
		}
        
		for (auto* h : _halfEdges) {
			if (h->Pair()->Pair() != h)
				return false;
		}

		return true;
	}

	template<typename Traits>
	bool TMesh<Traits>::IsTriMesh() const {
		if (!IsValid())
			return false;

		for (auto* f : _faces) {
			if (f->Degree() != 3)
				return false;
		}

		return true;
	}

	template<typename Traits>
	const MeshTraits_F<Traits>* TMesh<Traits>::EraseVertex(TV* v) {
		if (v->IsOnBoundary()) {
			RemoveVertex(v);
			return nullptr;
		}

		auto* h = v->HalfEdge();
		while (h->Next()->End() == v) {
			h = h->RotateNext();
			if (h == v->HalfEdge()) {
				RemoveVertex(v);
				return nullptr;
			}
		}
		h = h->Next();

		RemoveVertex(v);
		return AddFace(h->NextLoop());
	}

	template<typename Traits>
	template<typename... Args>
	MeshTraits_V<Traits>* TMesh<Traits>::AddEdgeVertex(TE* e, Args&&... args) {
		// 准备变量
		auto* h01 = e->HalfEdge();
		auto* h10 = h01->Pair();

		auto* v0 = h01->Origin();
		auto* v1 = h10->Origin();

		auto v0d = v0->Degree();
		auto v1d = v1->Degree();

		auto* f01 = h01->Face();
		auto* f10 = h10->Face();

		auto* h02 = New<TH>();
		auto* h21 = New<TH>();
		auto* h12 = New<TH>();
		auto* h20 = New<TH>();

		auto* e02 = New<TE>();
		auto* e12 = New<TE>();

		auto* v2 = New<TV>(std::forward<Args>(args)...);

		// 基本设置
		if (v0->HalfEdge() == h01)
			v0->SetHalfEdge(h02);
		if (v1->HalfEdge() == h10)
			v1->SetHalfEdge(h12);
		v2->SetHalfEdge(h21);

		h02->SetNext(h21);
		h02->SetPair(h20);
		h02->SetOrigin(v0);
		h02->SetEdge(e02);
		h02->SetFace(f01);

		//h21->SetNext();
		h21->SetPair(h12);
		h21->SetOrigin(v2);
		h21->SetEdge(e12);
		h21->SetFace(f01);

		h12->SetNext(h20);
		h12->SetPair(h21);
		h12->SetOrigin(v1);
		h12->SetEdge(e12);
		h12->SetFace(f10);

		//h20->SetNext();
		h20->SetPair(h02);
		h20->SetOrigin(v2);
		h20->SetEdge(e02);
		h20->SetFace(f10);

		e02->SetHalfEdge(h02);
		e12->SetHalfEdge(h12);

		if (!TF::IsBoundary(f01) && f01->HalfEdge() == h01)
			f01->SetHalfEdge(h02);
		if (!TF::IsBoundary(f10) && f10->HalfEdge() == h10)
			f10->SetHalfEdge(h12);

		// 四种情况
		if (v0d == 1 && v1d == 1) {
			h21->SetNext(h12);
			h20->SetNext(h02);
		}
		else if (v0d == 1) {
			auto* h01Next = h01->Next();
			auto* h10Pre = h10->Pre();

			h21->SetNext(h01Next);
			h20->SetNext(h02);
			h10Pre->SetNext(h12);
		}
		else if (v1d == 1) {
			auto* h01Pre = h01->Pre();
			auto* h10Next = h10->Next();

			h01Pre->SetNext(h02);
			h21->SetNext(h12);
			h20->SetNext(h10Next);
		}
		else {
			auto* h01Pre = h01->Pre();
			auto* h01Next = h01->Next();
			auto* h10Pre = h10->Pre();
			auto* h10Next = h10->Next();

			h01Pre->SetNext(h02);
			h10Pre->SetNext(h12);
			h21->SetNext(h01Next);
			h20->SetNext(h10Next);
		}

		// 删除
		Delete<TH>(h01);
		Delete<TH>(h10);
		Delete<TE>(e);

		return v2;
	}

	template<typename Traits>
	template<typename... Args>
	MeshTraits_E<Traits>* TMesh<Traits>::ConnectVertex(TH* h0, TH* h1, Args&&... args) {
		assert(h0->Face() == h1->Face());

		auto* f = h0->Face();

		assert(!TF::IsBoundary(f));

		auto* v0 = h0->Origin();
		auto* v1 = h1->Origin();

		assert(v0 != v1 && !TV::IsConnected(v0, v1));

		RemoveFace(f);

		auto* h0Pre = h0->Pre();
		auto* h1Pre = h1->Pre();

		auto h0Loop = h0->NextTo(h1);
		auto h1Loop = h1->NextTo(h0);

		auto* e01 = New<TE>(std::forward<Args>(args)...);
		auto* h01 = New<TH>();
		auto* h10 = New<TH>();

		e01->SetHalfEdge(h01);

		h01->SetNext(h1);
		h01->SetPair(h10);
		h01->SetOrigin(v0);
		h01->SetEdge(e01);

		h10->SetNext(h0);
		h10->SetPair(h01);
		h10->SetOrigin(v1);
		h10->SetEdge(e01);

		h0Pre->SetNext(h01);
		h1Pre->SetNext(h10);

		h0Loop.push_back(h10);
		h1Loop.push_back(h01);

		AddFace(h0Loop);
		AddFace(h1Loop);

		return e01;
	}

	template<typename Traits>
	bool TMesh<Traits>::FlipEdge(TE* e) {
		assert(e != nullptr && !e->IsOnBoundary());

		// 准备工作
		auto* h01 = e->HalfEdge();
		auto* h10 = h01->Pair();
		auto* h02 = h10->Next();
		auto* h13 = h01->Next();
		auto* h01Pre = h01->Pre();
		auto* h10Pre = h10->Pre();
		auto* h02Next = h02->Next();
		auto* h13Next = h13->Next();

		auto* f01 = h01->Face();
		auto* f10 = h10->Face();

		auto* v0 = h01->Origin();
		auto* v1 = h01->End();
		auto* v2 = h02->End();
		auto* v3 = h13->End();

		// 修改变量
		if (v0->HalfEdge() == h01)
			v0->SetHalfEdge(h02);
		if (v1->HalfEdge() == h10)
			v1->SetHalfEdge(h13);

		auto* h23 = h01;
		auto* h32 = h10;

		h01Pre->SetNext(h02);

		h02->SetNext(h23);
		h02->SetFace(f01);

		h23->SetOrigin(v2);
		h32->SetOrigin(v3);

		h23->SetNext(h13Next);

		h10Pre->SetNext(h13);

		h13->SetNext(h32);
		h13->SetFace(f10);

		h32->SetNext(h02Next);

		if (f01->HalfEdge() == h13)
			f01->SetHalfEdge(h02);
		if (f10->HalfEdge() == h02)
			f10->SetHalfEdge(h13);

		return true;
	}

	template<typename Traits>
	template<typename... Args>
	MeshTraits_V<Traits>* TMesh<Traits>::SplitEdge(TE* e, Args&&... args) {
		assert(e != nullptr);

		auto* h01 = e->HalfEdge();
		auto* h10 = h01->Pair();

		assert((!h01->IsOnBoundary() || !h10->IsOnBoundary())
			&& "two side of edge are boundaries");

		if (h01->IsOnBoundary() || h10->IsOnBoundary()) {
			if (h01->IsOnBoundary())
				std::swap(h01, h10);

			auto* f01 = h01->Face();

			assert(f01->Degree() == 3);

			/*
			*     v1         v1
			*    / |        / |
			*   /  |       /  |
			* v2   | ==> v2--v3
			*   \  |       \  |
			*    \ |        \ |
			*     v0         v0
			*/


			// 准备工作
			auto* h12 = h01->Next();
			auto* h20 = h12->Next();
			auto* h10Next = h10->Next();
			auto* v0 = h01->Origin();
			auto* v1 = h10->Origin();
			auto* v2 = h20->Origin();

			// 更新变量
			auto* h03 = h01;
			auto* h13 = h10;
			auto* e03 = e;
			auto* f03 = f01;

			// 创建对象
			auto* v3 = New<TV>(std::forward<Args>(args)...);
			auto* h30 = New<TH>();
			auto* h31 = New<TH>();
			auto* h32 = New<TH>();
			auto* h23 = New<TH>();
			auto* e32 = New<TE>();
			auto* e13 = New<TE>();
			auto* f31 = New<TF>();

			// 设置变量
			v3->SetHalfEdge(h31);

			h12->SetNext(h23);
			h12->SetFace(f31);
			h03->Init(h32, h30, v0, e03, f03);
			h13->Init(h30, h31, v1, e13, nullptr);
			h30->Init(h10Next, h03, v3, e03, nullptr);
			h31->Init(h12, h13, v3, e13, f31);
			h32->Init(h20, h23, v3, e32, f03);
			h23->Init(h31, h32, v2, e32, f31);

			e03->SetHalfEdge(h03);
			e32->SetHalfEdge(h32);
			e13->SetHalfEdge(h13);

			if (f03->HalfEdge() == h12)
				f03->SetHalfEdge(h03);
			f31->SetHalfEdge(h31);

			return v3;
		}

		auto* f01 = h01->Face();
		auto* f10 = h10->Face();

		assert(f01->Degree() == 3 && f10->Degree() == 3);

		/*
		*     v1             v1
		*    /||\           /||\
		*   / || \         / || \
		* v2  ||  v3 ==> v2--v4--v3
		*   \ || /         \ || /
		*    \||/           \||/
		*     v0             v0
		*/

		// 准备工作
		auto* h12 = h01->Next();
		auto* h20 = h12->Next();
		auto* h03 = h10->Next();
		auto* h31 = h03->Next();
		auto* v0 = h01->Origin();
		auto* v1 = h10->Origin();
		auto* v2 = h20->Origin();
		auto* v3 = h31->Origin();

		// 更新变量
		auto* h04 = h01;
		auto* h14 = h10;
		auto* e04 = e;
		auto* f04 = f01;
		auto* f14 = f10;

		// 创建变量
		auto* v4 = New<TV>(std::forward<Args>(args)...);
		auto* h40 = New<TH>();
		auto* h41 = New<TH>();
		auto* h42 = New<TH>();
		auto* h24 = New<TH>();
		auto* h43 = New<TH>();
		auto* h34 = New<TH>();
		auto* e42 = New<TE>();
		auto* e14 = New<TE>();
		auto* e43 = New<TE>();
		auto* f41 = New<TF>();
		auto* f40 = New<TF>();

		// 设置变量
		v4->SetHalfEdge(h41);

		h12->SetNext(h24);
		h12->SetFace(f41);
		h03->SetNext(h34);
		h03->SetFace(f40);
		h04->Init(h42, h40, v0, e04, f04);
		h14->Init(h43, h41, v1, e14, f14);
		h40->Init(h03, h04, v4, e04, f40);
		h41->Init(h12, h14, v4, e14, f41);
		h42->Init(h20, h24, v4, e42, f04);
		h24->Init(h41, h42, v2, e42, f41);
		h43->Init(h31, h34, v4, e43, f14);
		h34->Init(h40, h43, v3, e43, f40);

		e04->SetHalfEdge(h04);
		e42->SetHalfEdge(h42);
		e14->SetHalfEdge(h14);
		e43->SetHalfEdge(h43);

		if (f04->HalfEdge() == h12)
			f04->SetHalfEdge(h04);
		if (f14->HalfEdge() == h03)
			f14->SetHalfEdge(h14);
		f41->SetHalfEdge(h41);
		f40->SetHalfEdge(h40);

		return v4;
	}

	template<typename Traits>
	bool TMesh<Traits>::IsCollapsable(const TE* e) const {
		assert(e != nullptr);

		auto* h01 = e->HalfEdge();
		auto* h10 = h01->Pair();

		auto* v0 = h01->Origin();
		auto* v1 = h01->End();

		if (v0->IsOnBoundary() && v1->IsOnBoundary() && !e->IsOnBoundary())
			return false;

		size_t cnt = 2;
		if (h01->FaceDegree() > 3)
			--cnt;
		if (h10->FaceDegree() > 3)
			--cnt;

		for (auto* h0 : v0->OutHalfEdges()) {
			auto* h0end = h0->End();
			for (auto* h1 : v1->OutHalfEdges()) {
				if (h0end == h1->End()) {
					if (cnt == 0)
						return false;
					--cnt;
				}
			}
		}

		return true;
	}

	template<typename Traits>
	template<typename... Args>
	MeshTraits_V<Traits>* TMesh<Traits>::CollapseEdge(TE* e, Args&&... args) {
		assert(IsCollapsable(e) && "use IsCollapsable before CollapseEdge");

		auto* h01 = e->HalfEdge();
		auto* h10 = h01->Pair();

		auto* v0 = h01->Origin();
		auto* v1 = h01->End();

		if (v0->IsOnBoundary() && v1->IsOnBoundary() && !e->IsOnBoundary())
			return nullptr;

		if (v0->Degree() == 1) {
			EraseVertex(v0);
			return v1;
		}

		if (v1->Degree() == 1) {
			EraseVertex(v1);
			return v0;
		}

		auto* f01 = h01->Face();
		auto* f10 = h10->Face();
		size_t f01D = h01->FaceDegree();
		size_t f10D = h10->FaceDegree();

		// 设置 v
		auto* v = New<TV>(std::forward<Args>(args)...);
		if(h01->Pre()->Pair()->Face() != f10)
			v->SetHalfEdge(h01->Pre()->Pair());
		else
			v->SetHalfEdge(h10->Pre()->Pair());

		for (auto* h : v0->OutHalfEdges())
			h->SetOrigin(v);
		for (auto* h : v1->OutHalfEdges())
			h->SetOrigin(v);

		if (f01D == 3) { // f01->Degree() == 3
			auto* h01Next = h01->Next();
			auto* h01Pre = h01->Pre();
			auto* h01NextPair = h01Next->Pair();
			auto* h01PrePair = h01Pre->Pair();

			auto* v2 = h01Pre->Origin();
			if (v2->HalfEdge() == h01Pre)
				v2->SetHalfEdge(h01NextPair);

			auto* newE = New<TE>();

			h01NextPair->SetPair(h01PrePair);
			h01NextPair->SetEdge(newE);
			h01PrePair->SetPair(h01NextPair);
			h01PrePair->SetEdge(newE);
			newE->SetHalfEdge(h01NextPair);

			Delete<TE>(h01Next->Edge());
			Delete<TE>(h01Pre->Edge());
			Delete<TH>(h01Next);
			Delete<TH>(h01Pre);
			if (!TF::IsBoundary(f01))
				Delete<TF>(f01);
		}
		else { //f01->Degree >= 4
			if (!TF::IsBoundary(f01) && f01->HalfEdge() == h01)
				f01->SetHalfEdge(h01->Next());
			h01->Pre()->SetNext(h01->Next());
		}

		if (f10D == 3) { // f10->Degree() == 3
			auto* h10Next = h10->Next();
			auto* h10Pre = h10->Pre();
			auto* h10NextPair = h10Next->Pair();
			auto* h10PrePair = h10Pre->Pair();

			auto* v2 = h10Pre->Origin();
			if (v2->HalfEdge() == h10Pre)
				v2->SetHalfEdge(h10NextPair);

			auto* newE = New<TE>();

			h10NextPair->SetPair(h10PrePair);
			h10NextPair->SetEdge(newE);
			h10PrePair->SetPair(h10NextPair);
			h10PrePair->SetEdge(newE);
			newE->SetHalfEdge(h10NextPair);

			Delete<TE>(h10Next->Edge());
			Delete<TE>(h10Pre->Edge());
			Delete<TH>(h10Next);
			Delete<TH>(h10Pre);
			if (!TF::IsBoundary(f10))
				Delete<TF>(f10);
		}
		else { //p10->Degree >= 4
			if (!TF::IsBoundary(f10) && f10->HalfEdge() == h10)
				f10->SetHalfEdge(h10->Next());
			h10->Pre()->SetNext(h10->Next());
		}

		Delete<TV>(v0);
		Delete<TV>(v1);
		Delete<TH>(h01);
		Delete<TH>(h10);
		Delete<TE>(e);

		return v;
	}
}
